问题描述
简单来说,就是一个项目在 IDEA 中可以正常运行,打成 jar 包之后,通过 java -jar
在命令行运行就出错。
具体来说:
这是一个 Spring Boot 项目,在 service 层发出一个 HTTP 请求。
在 IDEA 运行的时候,能得到正确的 json 数据,在命令行中就只得到“未知错误”的提示。
解决过程
HTTP request
既然是 HTTP 的 response 不同,首先想到的是比较一下两处的 request 有何不同。
查看 okhttp 中请求主体的方法:
1 | private static String bodyToString(final Request request){ |
然而,两种方式发出的 HTTP request 好像都很正常,除去一些会变化的字符串外,请求主体一模一样。(这里会变化的字符串指的是时间戳、MD5 加密得到的字符串等,它们每次运行都是不一样的值)
此时应该将字符串写死,再对比两者的不同。
但是因为改起来太麻烦,加上以前的经验,我选择对请求主体中的字符进行 URL 编码,事实证明这是一个愚蠢的行为,它导致了在 IDEA 中也得不到想要的结果。
第一次尝试宣告失败。
为 okhttp 设置代理
使用下面的方式创建 OkHttpClient 就能在 Fidder 中捕获到 okhttp 发送和接收的数据了。
1 | String hostname = "localhost"/*127.0.0.1*/; |
这里的思路是:不单单对比请求主体,而是通过 Fidder 更细致地比较两次 HTTP 请求的区别。
确实可以捕获到程序发出的 HTTP 请求,但是跟刚才一样,除去一些会变化的值,两者几乎是一样的。
第二次尝试也没有发现有用的信息。
MD5
还是回到最初的疑惑:明明是一样的代码,而且是同一台电脑上,为什么得到不一样的结果?
同样的代码,难道会计算出不一样的 Hash Value ?
是的。
可能是直觉,也可能是实在没有思路了,所以猜测是 sign 字段(通过 MD5 算法生成)的问题。
很快验证出的确是 sign 字段有问题,但是怎么解决呢?
因为不知道在命令行运行时怎么 debug,所以一切都变得很麻烦。
经过慢慢的试验,一步步打印出来,最后发现进行摘要的字符串其实是一样的,但是得到的结果却不一样。
很明显是摘要算法有问题。
这个方法是我在网上复制的,难道它在命令行中执行会得到不一样的结果?
我又在网上搜了其他的 MD5 代码,但是还是一样出错。(如果此时运行对了,可能我永远都不会知道问题真正的原因了)。
最终用我的工地英语在谷歌搜索:java cmd md5 digest difference
,然后看到这篇回答,才知道因为 MD5 算法中,要将字符串转换成 byte[],而 IDEA 和命令行的编码不同,导致得到的 byte[] 不同,进而导致 sign 不同。
在计算 Hash Value 的时候指定编码。
1 | byte[] array = md.digest(str.getBytes("UTF-8")); |
总结
虽然解决这个问题只需要改一行代码,但是整个过程却花了我很多时间,做了太多太多无谓的尝试。
不过还好,还是解决了。